package com.wyw.function.learning.demo9;

import com.wyw.function.learning.common.Discount;
import com.wyw.function.learning.common.Quote;
import com.wyw.function.learning.common.Shop;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @Title   对多个异步任务进行流水线操作
 * @Description     构建同步和异步操作
 * @Author Mr Wu yewen.wu.china@gmail.com
 * Update History:
 * Author        Time            Content
 */
public class DemoClient25 {

    static final List<Shop> shops =
            Arrays.asList(
                    new Shop("wyw"),
                    new Shop("hello"),
                    new Shop("one"),
                    new Shop("two")
            );

    public static void main(String[] args) {
        log(() -> findPrices("wyw"));
    }


    public static List<String> findPrices(String product) {
        ExecutorService ex = Executors.newCachedThreadPool();

        List<CompletableFuture<String>> priceFutures = shops.stream()
                // 异步方式取得每个shop中指定产品的原始价格
                .map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), ex))
                // 使用另外一个异步任务工作期望的Future，申请折扣
                .map(future -> future.thenApply(Quote::parse))// 一般转换成订单不需要任何远程服务，也不会进行任何I/O操作，几乎可以第一时间进行，不会带来太多延迟

                // thenCompose允许对两个异步操作进行流水线，第一个操作完成时，将其结果作为参数传递给第二个操作，也就是说，可以创建两个CompletableFuture对象，对第一个对象调用thenCompose，并向其传递一个函数
                // 当第一个CompletableFuture执行完毕后，他的结果将作为函数的参数，这个函数的返回值是以第一个CompletableFuture执行完毕后，它的结果将作为该函数的参数，着这个函数的返回值是以第一个CompletableFuture的返回做输入计算出的第二个CompletableFuture对象
                // 使用该方式，即使Future在向不同的商店收集报价，主线程还是可以执行其他的重要操作，比如响应UI事件
                // thenComposeAsync在开新线程，使用thenCompose是因为更高效一些，少了线程切换的开销
                // 不需要等待第一个的情况使用thenCombine方法
                .map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), ex)))// 设计到远程操作，就会u有延迟，所以希望异步执行
                .collect(Collectors.toList());

        return priceFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
    }

    private static<T> T log(Supplier<T> supplier) {
        long start = System.currentTimeMillis();
        T t = supplier.get();
        System.out.println(System.currentTimeMillis() - start + ":ms");
        return t;
    }
}

